﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Xml.Serialization;
using ScrumTable.Common.Collections;
using ScrumTable.Common.Security;
using ScrumTable.DL.Data;

namespace ScrumTable.Config.UserManagement
{
    #region Usings

    

    #endregion

    /// <summary>
    /// Represents a LoginAccount
    /// </summary>            
    public class LoginAccount : ILoginAccount
    {

        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------
        private List<SerializablePair<string, string>> _deCryptedLogins;
        private List<string> _hidenMembers = new List<string>();

        /// <summary>
        /// If a User has this password, his password gets not overwritten (used for LoginChange)
        /// </summary>
        public const string HiddenPassword = "$3_DEFAULT_2£";

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        /// <summary>
        /// Gets or sets the identity tag value.
        /// </summary>
        /// <value>The identity tag value.</value>
        [XmlAttribute]
        public long IdentityTagValue { get; set; }

        /// <summary>
        /// A list of all logins associated with this LoginAccount
        /// </summary>
        [XmlArray("Logins")]
        [XmlArrayItem(Type = typeof(SerializablePair<string, string>), ElementName = "Login")]
        public List<SerializablePair<string, string>> CryptedLogins { get; set; }

       

        /// <summary>
        /// Reset after each get
        /// Use <see cref="Decrypt"/> for each use.
        /// </summary>
        /// <value>list of all logins.</value>
        [XmlIgnore]
        public List<SerializablePair<string, string>> DeCryptedLogins
        {
            get
            {

                var tmp = new List<SerializablePair<string, string>>(_deCryptedLogins);
                if (CryptedLogins.Count > 0)
                {
                    _deCryptedLogins.Clear();
                    IsEncrypted = true;
                }                
                return tmp;

            }
            set
            {
                _deCryptedLogins = value;
            }
        }

        /// <summary>
        /// First LoginName in the List
        /// </summary>
        [XmlIgnore]
        public string ServerLoginName
        {
            get
            {
                if (CryptedLogins.Count > 0)
                {
                    return CryptedLogins[0].Left;
                }
                return "";
            }
        }
        /// <summary>
        /// Gets or sets the name of the server.
        /// </summary>
        /// <value>The name of the server.</value>
        [XmlAttribute]
        public string ServerName { get; set; }


        /// <summary>
        /// Gets or sets the type of the data.
        /// </summary>
        /// <value>The type of the data.</value>
        [XmlAttribute]
        public string DataType { get; set; }

        /// <summary>
        /// Gets or sets the config for the data adapter.
        /// </summary>
        /// <value>The config forthe data adapter.</value>
        
        [XmlArray("DataConfigs")]
        [XmlArrayItem(Type = typeof(SerializablePair<string, string>), ElementName = "DataConfig")]
        public List<SerializablePair<string, string>> DataConfigName { get; set; }

        /// <summary>
        /// Gets the a list of members which are hidden in the application (example: the admin user) 
        /// </summary>
        /// <value>The hidden members.</value>
        [XmlArray("HiddenMembers")]
        [XmlArrayItem(Type = typeof(string), ElementName = "HiddenMembers")]
        public List<string> HiddenMembers { get { return _hidenMembers; } set { _hidenMembers = value; } }

        /// <summary>
        /// Gets a list with member name associated to a color
        /// </summary>
        /// <value>color list.</value>
        [XmlArray("MemberColors")]
        [XmlArrayItem(Type = typeof(SerializablePair<string, string>), ElementName = "MemberColor")]
        public List<SerializablePair<string, string>> MemberColors { get; set; }
 
        /// <summary>
        /// Gets or sets the id of the config file.
        /// </summary>
        [XmlAttribute]
        public int ConfigId { get; set; }
     
        /// <summary>
        /// Gets or sets the last used screen.
        /// </summary>
        /// <value>The last used screen.</value>
        [XmlAttribute]
        public string LastUsedScreen { get; set; }

        /// <summary>        
        /// Default TFS-Project
        /// </summary>
        /// <value>The name of the project.</value>
        [XmlAttribute]
        public string ProjectId { get; set; }

        /// <summary>
        /// Default TFS-Iteration
        /// </summary>
        /// <value>The name of the iteration.</value>
        [XmlAttribute]
        public string IterationId { get; set; }

        /// <summary>
        /// Is the server password encrypted
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is encrypted; otherwise, <c>false</c>.
        /// </value>
        [XmlAttribute]
        public bool IsEncrypted { get; set; }


        /// <summary>
        /// If the Pin is set the server password gets encrypted. 
        /// The pin will not be stored.
        /// </summary>
        [XmlIgnore] 
        public string Pin
        {
            set
            {
               Encrypt(value);
            }
        }


        /// <summary>
        /// Gets the Icon for the data connection type
        /// </summary>
        [XmlIgnore]
        public Bitmap DataConnectionIcon
        {
            get
            {
                foreach (var x in DataConnectionFactory.CachedDataAssemblies)
                {
                    if (x.Description == DataType)
                    {
                        return x.AssemblyBitmap;
                    }
                }
                return null;
            }
        }


        /// <summary>
        /// Gets the Icon for the data connection type
        /// </summary>
        [XmlIgnore]
        public bool IsLoginRequired
        {
            get
            {
                foreach (var x in DataConnectionFactory.CachedDataAssemblies)
                {
                    if (x.Description == DataType)
                    {

                        return x.AssemblyRequiresStrongAuthentication;
                    }
                }
                return true;
            }
        }


        /// <summary>        
        /// Selected Schema
        /// </summary>
        /// <value>The name of the project.</value>
        [XmlAttribute]
        public string SchemaName { get; set; }

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------


        /// <summary>
        /// Initializes a new instance of the <see cref="LoginAccount"/> class.
        /// </summary>
        public LoginAccount()
        {
            IsEncrypted = false;
            DeCryptedLogins  = new List<SerializablePair<string, string>>();
            CryptedLogins = new List<SerializablePair<string, string>>();
            MemberColors = new List<SerializablePair<string, string>>();
            DataConfigName = new List<SerializablePair<string, string>>();
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Get config for a data assembly type of connection
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public string GetConfigForConnection(string type)
        {
            if (type == null) return null;
            var value = DataConfigName.FirstOrDefault(x => x.Left == type);
            return value == null ? null : value.Right;
        }

        /// <summary>
        /// sets the config for a data assembly type
        /// </summary>
        /// <param name="type"></param>
        /// <param name="config"></param>
        public void SetConfigForConnection(string type, string config)
        {
            var value = DataConfigName.FirstOrDefault(x => x.Left == type);
            if(value == null)
            {
                DataConfigName.Add(new SerializablePair<string,string>(type, config));
            }
            else
            {
                value.Right = config;
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance can login.
        /// </summary>
        /// <value><c>true</c> if this instance can login; otherwise, <c>false</c>.</value>
        public bool CanLogin
        {
            get
            {
                bool password = (CryptedLogins.Count==0 && _deCryptedLogins.Count==0);

                return !(password || string.IsNullOrEmpty(ServerName) ||
                       string.IsNullOrEmpty(CryptedLogins[0].Right));
            }
        }
  
        /// <summary>
        /// Encrypts (veschlüsslet) the server password
        /// </summary>
        /// <param name="pin">Encryption pin</param>
        /// <returns></returns>
        public bool Encrypt(string pin)
        {
            if (_deCryptedLogins.Count>0)
            {
                CryptedLogins.Clear();
                foreach (var x in _deCryptedLogins)
                {
                    string pwd = AESEncryption.Encrypt(x.Right, pin,
                                                           MD5Encryption.CreateMD5Hash(pin), 2,
                                                           "16CHARSLONG12345", 256);
                    CryptedLogins.Add(new SerializablePair<string, string>(x.Left, pwd));
                    IsEncrypted = true;
                }
                _deCryptedLogins.Clear();
            }
            return true;
        }

        /// <summary>
        /// Sets the logins for this account 
        /// </summary>
        /// <param name="name">Loginname for the server</param>
        /// <param name="pwd">Password for the server</param>
        public void SetLogin(string name, string pwd)
        {
            SetLogins(new List<SerializablePair<string, string>>() {new SerializablePair<string, string>(name, pwd)});
        }

        /// <summary>
        /// Sets a list of login accounts for this account
        /// </summary>
        /// <param name="logins">list of login accounts</param>
        public void SetLogins(List<SerializablePair<string, string>> logins)
        {
           // CryptedLogins.Clear();
            IsEncrypted = false;
            _deCryptedLogins = new List<SerializablePair<string, string>>(logins);
        }

        /// <summary>
        /// Changes the logins. For this must the old list first be decrypted
        /// </summary>
        /// <param name="logins">the new login list</param>
        /// <param name="oldPin">the pin from the other logins</param>
        public void ChangeLogins(List<SerializablePair<string, string>> logins, string oldPin)
        {

            List<SerializablePair<string, string>> clonedItems = (from x in logins select new SerializablePair<string, string>(x.Left, x.Right)).ToList();
            Decrypt(oldPin);

            foreach (var newLogin in clonedItems)
            {
                if (newLogin.Right == HiddenPassword)
                {
                    var old = _deCryptedLogins.FirstOrDefault(x => x.Left == newLogin.Left);
                    if (old != null)
                    {

                        newLogin.Right = old.Right;
                    }
                }
            }
            SetLogins(clonedItems);
        }

        /*
        // <summary>
        // Adds a new account to the list of user. For this must the old list first be decrypted
        // </summary>
        // <param name="newLogin">the new login</param>
        // <param name="oldPin">the pin from the other logins</param>
        public void AddLogin(SerializablePair<string, string> newLogin, string oldPin)
        {
            if (_deCryptedLogins.Count == 0)
            {
                Decrypt(oldPin);
            }
            _deCryptedLogins.Add(newLogin);
        }*/
        

        /// <summary>
        /// Decrypts the server password.
        /// </summary>
        /// <param name="pin">Decryption pin</param>
        /// <returns></returns>
        public bool Decrypt(string pin)
        {
            

            if (IsEncrypted && CryptedLogins.Count>0)
            {
                _deCryptedLogins.Clear();

                foreach (var x in CryptedLogins)
                {
                    string pwd = AESEncryption.Decrypt(x.Right, pin, MD5Encryption.CreateMD5Hash(pin), 2,
                                                       "16CHARSLONG12345", 256);
                    _deCryptedLogins.Add(new SerializablePair<string, string>(x.Left, pwd));
                    IsEncrypted = false;}
                
            }
            return true;
        }
        
        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        #endregion

    }
}
